// data_structures.cpp
// Customizer_Base
// Protea Customizer/Customizer Pro / Business Logic for managing data structures (buffers)
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// Please send correspondence to:
//
// theproteaproject@gmail.com
//

#include "base_setup.h"
#include "../../../Fields/field_defns.h"
#include <iostream>


//@ SEC 3
//@ CHA GetNextSequenceNbr
//@ COD GetNextSequenceNbr
//@ GEN Returns the next available sequence number
void GetNextSequenceNbr( XCBuffer* ds, XCTransactionVars* vars ) {
	TRANS_INFUNC( GetNextSequenceNbr );

	XCSQLGen* query = vars->query;
	XCDBStatement* stmt = NULL;
	XCBuffer* dstemp = NULL;
	XCCompare comp[2];
	XCField* list[3];
	XCOrder order;

	try {
		//@ Create a copy of the supplied ds buffer
		dstemp = ds->Copy();

		//@ Create a search for the ds record in this format with highest seq. nbr
		list[0] = ds->GetField( FLD_FORMAT_NUMBER );
		list[1] = ds->GetField( FLD_APPL_NBR );
		list[2] = NULL;

		order.direction = order_desc;

		query->MakeSELECT( dstemp );
		query->AddWHERE( list, comp );
		query->AddORDERBY( FLD_SEQUENCE_NBR, &order );
		stmt = vars->dbtxn->ExecuteSQL( query );

		//@ If no results,
		if (stmt->Eof()) {
			//@ GRS
			//@ Set sequence nbr to zero
			ds->GetField( FLD_SEQUENCE_NBR )->SetLong( 0 );
			//@ GRE
		} else {
			//@ GRS
			XCField* temp = ds->GetField( FLD_SEQUENCE_NBR );

			//@ Set sequence nbr to retrieved value
			temp->MoveField( dstemp->GetField( FLD_SEQUENCE_NBR ) );

			//@ Add one to that value
			temp->AddConst( 1 );

			//@ GRE
		};
	} catch( ... ) {
		vars->myapp->CleanupObjects( 2, &stmt, &dstemp );
		throw;
	};
	vars->myapp->CleanupObjects( 2, &stmt, &dstemp );

	TRANS_OUTFUNC();
};




//@ SEC 3
//@ CHA DSGetFieldFormatNames
//@ COD DSGetFieldFormatNames
//@ GEN If a field name is given, translate the field name into a field number

void DSGetFieldFormatNames( XCTransactionVars* vars, XCBuffer* buffer, bool get_field, XCBuffer* format_buffer, XCBuffer* field_buffer ) {
	XCField* temp;

	//@ UTL LookupNumberFromName to convert format name into format number
	temp = vars->buffer->GetField( FLD_FORMAT_NAME );
	if (temp && !temp->IsBlank()) {
		if (!LookupNumberFromName( vars, vars->buffer, buffer, FLD_FORMAT_NUMBER, FLD_FORMAT_NAME, FMT_FORMAT_DESCRIPTR ))
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_ERROR, FLD_FORMAT_NAME, 1, ERR_FORMAT ); 
	} else {
		temp = vars->buffer->GetField( FLD_FORMAT_NUMBER );
		if (!temp || temp->IsBlank())
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_ERROR, FLD_FORMAT_NAME, 1, ERR_FORMAT ); 
	};

	if (get_field) {
		//@ UTL LookupNumberFromName to convert field name into field number
		temp = vars->buffer->GetField( FLD_FIELD_NAME );
		if (temp && !temp->IsBlank()) {
			if (!LookupNumberFromName( vars, vars->buffer, buffer, FLD_FIELD_NUMBER, FLD_FIELD_NAME, FMT_FIELD_DESCRIPTOR ))
				vars->Errors->Add( ERR_DOESNT_EXIST, SEV_ERROR, FLD_FIELD_NAME, 1, ERR_FIELD ); 
		} else {
			temp = vars->buffer->GetField( FLD_FIELD_NUMBER );
			if (!temp || temp->IsBlank())
				vars->Errors->Add( ERR_DOESNT_EXIST, SEV_ERROR, FLD_FIELD_NAME, 1, ERR_FIELD ); 
		};
	};

	if (format_buffer) {
		//@ Move corresponding data from the transaction buffer
		format_buffer->MoveField( buffer, FLD_FORMAT_NUMBER );
		format_buffer->MoveField( buffer, FLD_APPL_NBR );

		//@ Retrieve the format descriptor record
		if (!FormatDescRetrieve( format_buffer, vars ))
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_ERROR, FLD_FORMAT_NAME, 1, ERR_FORMAT ); 
	};

	if (field_buffer) {
		//@ Move corresponding data from the transaction buffer
		field_buffer->MoveField( buffer, FLD_FIELD_NUMBER );
		field_buffer->MoveField( buffer, FLD_APPL_NBR );

		//@ Retrieve the field descriptor record
		if (!FieldDescRetrieve( field_buffer, vars ))
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_ERROR, FLD_FIELD_NAME, 1, ERR_FIELD ); 
	};
};




//@ SEC 3
//@ CHA Data Structure Add
//@ COD DataStructureAdd
//@ TAB Data_Structures Write
//@ GEN Use DataStructureAdd to add a new data structure record

void DataStructureAdd( XCBuffer* input, XCBuffer* format, XCBuffer* field, XCTransactionVars* vars ) {
	TRANS_INFUNC( DataStructureAdd );

	XCSQLGen* query = vars->query;

	// Mark the data-structure as changed
	input->GetField( FLD_APPDIC_VERSION )->SetLong( 1 );

	// FIND THE FREAKING BUG
	if (input->GetField( FLD_FIELD_NUMBER )->GetLong() != 0 ||
		input->GetField( FLD_FORMAT_NUMBER )->GetLong() != 0) {
		if (format && field) {
		//@ SUB FixFieldType Ensure valid field types, etc.
			FixFieldType( format, field, input, vars );
		};

		//@ Add the actual record now
		query->MakeFull( SQL_STMT_INSERT, input );
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );
	} else
		assert( false );

	TRANS_OUTFUNC();
};




//@ SEC 3
//@ CHA Data Structure Change
//@ COD DataStructure Change
//@ TAB Data_Structures Write
//@ GEN Use DataStructureChange to alter a data structure record

void DataStructureChange( XCBuffer* input, XCBuffer* compare, XCTransactionVars* vars ) {
	TRANS_INFUNC( DataStructureChange );

	XCSQLGen* query = vars->query;

	input->GetField( FLD_APPDIC_VERSION )->SetLong( 1 );

	//@ Change the actual record now
	query->MakeFull( SQL_STMT_UPDATE, input, compare );
	vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

	TRANS_OUTFUNC();
};





//@ SEC 3
//@ CHA Data Structure Retrieve
//@ COD DataStructureRetrieve
//@ TAB Data_Structures Read
//@ GEN Use DataStructureRetrieve to retrieve a data structure record

bool DataStructureRetrieve( XCBuffer* ds, XCTransactionVars* vars ) {
	TRANS_INFUNC( DataStructureRetrieve );

	XCDBStatement* stmt = NULL;
	XCSQLGen* query = vars->query;
	bool result;

	//@ Check if DATA_STRUCTURES record exists.
	ds->DebugDump();
	query->MakeFull( SQL_STMT_SELECT, ds );

	stmt = vars->dbtxn->ExecuteSQL( query );
	result = (!stmt->Eof());
	App->CleanupObjects( 1, &stmt );

	TRANS_OUTFUNCRET( result );
};





//@ SEC 3
//@ CHA Data Structure Review Format
//@ COD DataStructureReviewFormat
//@ TAB Data_Structures Read
//@ GEN Check if DATA_STRUCTURES record exists for a particular format

XCDBStatement* DataStructureReviewFormat( XCBuffer* ds, XCTransactionVars* vars ) {
	TRANS_INFUNC( DataStructureReviewFormat );

	XCSQLGen* query = vars->query;

	XCDBStatement* stmt = NULL;
	XCCompare comp[2];
	XCField* list[3];
	XCOrder order;

	list[0] = ds->GetField( FLD_FORMAT_NUMBER );
	list[1] = ds->GetField( FLD_APPL_NBR );
	list[2] = NULL;

	query->MakeSELECT( ds );
	query->AddWHERE( list, comp );
	query->AddORDERBY( FLD_SEQUENCE_NBR, &order );
	stmt = vars->dbtxn->ExecuteSQL( query );

	TRANS_OUTFUNCRET( stmt );
};




//@ SEC 3
//@ CHA Data Structure Review Field
//@ COD DataStructureReviewField
//@ TAB Data_Structures Read
//@ GEN Check if DATA_STRUCTURES record exists for a particular field (where used)

XCDBStatement* DataStructureReviewField( XCBuffer* ds, XCTransactionVars* vars ) {
	TRANS_INFUNC( DataStructureReviewField );

	XCSQLGen* query = vars->query;

	XCCompare comp[2];
	XCField* list[3];
	XCOrder order;

	list[0] = ds->GetField( FLD_FIELD_NUMBER );
	list[1] = ds->GetField( FLD_APPL_NBR );
	list[2] = NULL;

	query->MakeSELECT( ds );
	query->AddWHERE( list, comp );
//	query->AddORDERBY( FLD_FORMAT_NUMBER, &order );

	TRANS_OUTFUNCRET( vars->dbtxn->ExecuteSQL( query ) );
};



//@ SEC 3
//@ CHA Data Structure Verify
//@ COD DataStructureVerify
//@ TAB Data_Structures Read
//@ GEN Use DataStructureVerify to verify data structure record

void DataStructureVerify( XCBuffer* input, verify_type verify_mode, XCBuffer* format, XCBuffer* field, XCTransactionVars* vars, bool pro_mode, XCBuffer* original ) {
	TRANS_INFUNC( DataStructureVerify );

	//@ If verifying an ADD or CHANGE transaction,
	//@ GRS
	if (verify_mode == VERIFY_ADD || verify_mode == VERIFY_CHANGE) 
	{
		long type = input->GetField( FLD_DATA_TYPE )->GetLong( true );

		//@ Ensure data type range
		if (!input->GetField( FLD_DATA_TYPE )->CheckInRange( MIN_TYPE, MAX_TYPE ))
			vars->Errors->Add( ERR_DATA_TYPE, SEV_CRITICAL );

        const tdata_type_defnition* data_type = GetDataTypeDefn( type );

		App->ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "******** input->GetField( FLD_DATA_TYPE )->Compare( VAR_STRING ) : %ld", input->GetField( FLD_DATA_TYPE )->Compare( VAR_STRING ) );
		//@ Check that dec places is in the range of 0-254
		//@ ERR E (ERR_INV_SHORT_RANGE) Must be in the range of 0 to 254
		if (!input->GetField( FLD_DECIMAL_PLACES )->CheckInRange( 0, 254 ))
			vars->Errors->Add( ERR_INV_SHORT_RANGE, SEV_ERROR, FLD_DECIMAL_PLACES, 3, input->GetField( FLD_DECIMAL_PLACES ), 0, 254 );

		//@ Check that min length is in the range of 0-254
		//@ ERR E (ERR_INV_SHORT_RANGE) Must be in the range of 0 to 254
		if (!input->GetField( FLD_MIN_LENGTH )->CheckInRange( 0, data_type->max_size ))
			vars->Errors->Add( ERR_INV_SHORT_RANGE, SEV_ERROR, FLD_MIN_LENGTH, 3, input->GetField( FLD_MIN_LENGTH ), 0, data_type->max_size );

		//@ All datatypes, other than VAR_STRING and BLOB have a max length limit
		if (type != VAR_STRING && type != XCBLOB) {
			//@ GRS
			//@ Check that max length is in the range of 0-254 (zero max length means use field_descriptor default)
			//@ ERR E (ERR_INV_SHORT_RANGE) Must be in the range of 0 to 254
			if (!input->GetField( FLD_MAX_LENGTH )->CheckInRange( data_type->min_size, data_type->max_size ))
				vars->Errors->Add( ERR_INV_SHORT_RANGE, SEV_ERROR, FLD_MAX_LENGTH, 3, input->GetField( FLD_MAX_LENGTH ), data_type->min_size, data_type->max_size );
			//@ GRE
		};
		//@ GRE

		//@ Ensure MIN_LENGTH <= MAX_LENGTH, if not display err:
		//@ ERR E (ERR_MAX_GREATER) MIN_LENGTH must be greater than MAX_LENGTH
		if (input->GetField( FLD_MIN_LENGTH )->CompareField( input->GetField( FLD_MAX_LENGTH ) )>0)
			vars->Errors->Add( ERR_MAX_GREATER );
	};
	//@ GRE


	////////////////////////////////////
	//
	// Start database verifications
	//
	////////////////////////////////////


	//@ If verifying an add transaction:
	if (verify_mode == VERIFY_ADD) {
		//@ GRS 
		//@ SUB DataStructuresRetrieve to ensure data structure doesn't exist
		//@ ERR E (ERR_INV_FIELD_FORMAT)
		if (DataStructureRetrieve( input, vars ))
			vars->Errors->Add( ERR_INV_FIELD_FORMAT, SEV_CRITICAL );
		//@ GRE
	} else {
		//@ Otherwise:
		//@ GRS
		//@ SUB DataStructuresRetrieve to ensure data structure already exists
		//@ ERR E (ERR_INV_DATASTRUCT)
		if (!DataStructureRetrieve( original ? original : input, vars ))
			vars->Errors->Add( ERR_INV_DATASTRUCT, SEV_CRITICAL );
		//@ GRE
	};


	//@ If verifying an ADD or CHANGE transaction,
	//@ GRS
	if (verify_mode == VERIFY_ADD || verify_mode == VERIFY_CHANGE) {
		//@ SUB FormatDescRetrieve to retrieve format descriptor record
		if (!FormatDescRetrieve( format, vars ))
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_ERROR, FLD_FORMAT_NAME, 1, 20001 );

		//@ If attempting to make changes on a database buffer, other than field sequence, error out
		if (verify_mode == VERIFY_CHANGE && format->GetField( FLD_FORMAT_TYPE )->GetLong(true) < 3) {

			// Override any changes to min/max length
			input->MoveField( original, FLD_MIN_LENGTH );
			input->MoveField( original, FLD_MAX_LENGTH );
			input->MoveField( original, FLD_APPDIC_VERSION );

			// Mark changes between buffers
			input->MarkChanges( original );

			input->DebugDump();
			for (int i=0; i<input->GetFieldCount(); i++) {
				XCField* field = input->GetFieldByIndex(i);
				if (field->IsChanged()) {
					if (field->GetFieldNumber() != FLD_SEQUENCE_NBR &&
						field->GetFieldNumber() != FLD_CRITICAL_FIELD &&
						field->GetFieldNumber() != FLD_DESCRIPTOR_FLAGS)
						vars->Errors->Add( ERR_DS_DB_CHANGE, SEV_CRITICAL );
				};
			};
		};

		//@ SUB FieldDescRetrieve to retrieve field descriptor record
		if (!FieldDescRetrieve( field, vars ))
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_ERROR, FLD_FIELD_NAME, 1, 20003 );

		//@ GRS Check for valid field type/format type combo
		XCField* temp = format->GetField( FLD_FORMAT_TYPE );

		bool IsScreen = (temp->Compare(3) == 0 || temp->Compare(6) == 0);
		bool IsDatabase = (temp->Compare(3) < 0);

		temp = input->GetField( FLD_DATA_TYPE );
		const tdata_type_defnition* defns = GetDataTypeDefn( temp->GetLong( true ) );
		if (IsDatabase) {
			//@ Don't allow display numerics in the database
			//@ ERR E (ERR_INV_FIELD_FORMAT_TYPE) asdfasdf
			if (defns->database_type != temp->GetLong( true ))
				vars->Errors->Add( ERR_INV_FIELD_FORMAT_TYPE );
		} else if (IsScreen) {
			//@ Don't allow anything but string and display numeric on screens
			//@ ERR E (ERR_INV_FIELD_FORMAT_TYPE) sadfasdf
			if (defns->screen_type != temp->GetLong( true ))
				vars->Errors->Add( ERR_INV_FIELD_FORMAT_TYPE );
		};
		//@ GRE

		//@ If MAX_LENGTH = 0, then use field_descriptor value for MAX_LENGTH
		if (input->GetField( FLD_MAX_LENGTH )->Compare(0)==0)
			input->MoveUnlikeField( FLD_MAX_LENGTH, field, FLD_DB_BYTE_LENGTH );

		//@ Check for DATA_STRUCTURES record with same FIELD_SEQUENCE
		XCField* compfield = input->GetField( FLD_SEQUENCE_NBR );
		if (compfield->Compare(0) != 0) {
			if (original)
				input->MarkChanges( original );

			if (verify_mode == VERIFY_CHANGE && !input->GetField( FLD_SEQUENCE_NBR )->IsChanged())
				compfield = NULL;

			if (compfield && SelectCountMany( FMT_DATA_STRUCTURES, vars, 3,
				input->GetField( FLD_FORMAT_NUMBER ),
				compfield,
				input->GetField( FLD_APPL_NBR )))

				vars->Errors->Add( ERR_INVALID_SEQUENCE, SEV_CRITICAL );
		};
	};

#ifndef DESIGN_MODE
	//@ If verifying a CHANGE or DELETE transaction, check critical field flags (NON-DESIGNER ONLY)
	if (verify_mode == VERIFY_CHANGE || verify_mode == VERIFY_DELETE) {
		//@ GRS

		//@ Check that this data structure is not a critical field
		//@ ERR C (ERR_CRITICAL_FIELD) Cannot change/delete critical field
		//@ ERR C (ERR_CRITICAL_FIELD_DEL) Cannot delete critical field
		int result = input->GetField( FLD_CRITICAL_FIELD )->Compare(1);
		if (result > 0)		// Critical field >= 2
			vars->Errors->Add( ERR_CRITICAL_FIELD, SEV_CRITICAL );
		else if (result == 0 && verify_mode == VERIFY_DELETE)
			vars->Errors->Add( ERR_CRITICAL_FIELD_DEL, SEV_CRITICAL );

		//@ GRE
	};
#endif
	//@ GRE

	TRANS_OUTFUNC();
};





XCDBStatement* GetFieldWhereUsed( XCBuffer* ds, XCTransactionVars* vars ) {
	XCCompare comp[2];
	XCField* list[3];
	XCOrder order;
	list[0] = ds->GetField( FLD_FIELD_NUMBER );
	list[1] = ds->GetField( FLD_APPL_NBR );
	list[2] = NULL;

	vars->query->MakeSELECT( ds );
	vars->query->AddWHERE( list, comp );
	return vars->dbtxn->ExecuteSQL( vars->query );
};




XCDBStatement* GetLitGroupWhereUsed( XCBuffer* ds, XCTransactionVars* vars ) {
	XCCompare comp[4];
	XCField* list[5];
	XCOrder order;
	XCDBStatement* stmt = NULL;

	XCField* datatype = ds->GetField( FLD_DATA_TYPE );
	XCField* copy = datatype->Clone();

	copy->SetLong( LITERAL_STRING );
	datatype->SetLong( LITERAL_VAL );

	list[0] = ds->GetField( FLD_APPL_NBR );

	list[1] = datatype;
	comp[1].preparen++;

	list[2] = copy;

	comp[2].postparen++;
	comp[2].logical = logic_or;

	list[3] = ds->GetField( FLD_DECIMAL_PLACES );
	list[4] = NULL;

	vars->query->MakeSELECT( ds );
	vars->query->AddWHERE( list, comp );

	try {
		stmt = vars->dbtxn->ExecuteSQL( vars->query );
	} catch( ... ) {
		copy->Release();
		throw;
	};
	copy->Release();

	return stmt;
};


// ---- SRM Transaction manual references ---
//@ SEC 2
//


void AddDataStructureInternal( XCTransactionVars* vars, bool pro_mode ) {
	// Initial setup	
	XCBuffer* TransBuf = NULL;
	XCBuffer* format_buffer = NULL, *field_buffer = NULL;
	XCDBStatement* stmt = NULL;

	try {
		//@ Create a transaction (DATA_STRUCTURES) buffer
		TransBuf = vars->myapp->CreateBuffer( FMT_DATA_STRUCTURES, true );
	
		//@ Move screen items into transaction buffer
		TransBuf->MoveCorresponding( vars->buffer, vars->Errors );

		//@ Check for validation errors
		vars->Errors->Check();

		//@ Create a FORMAT_DESCRIPTOR buffer
		format_buffer = vars->myapp->CreateBuffer( FMT_FORMAT_DESCRIPTR, true );

		//@ Create a FIELD_DESCRIPTOR buffer
		field_buffer = vars->myapp->CreateBuffer( FMT_FIELD_DESCRIPTOR, true );

		//@ SUB DSGetFieldFormatNames to translate names to numbers
		DSGetFieldFormatNames( vars, TransBuf, true, format_buffer, field_buffer );
		vars->Errors->Check();

		//@ If we were not supplied a data-type, use the field_descriptor data_type
		if (TransBuf->GetField( FLD_DATA_TYPE )->Compare(0)==0) {
			long type = field_buffer->GetField( FLD_DATA_TYPE )->GetLong( true );
			long ftype = format_buffer->GetField( FLD_FORMAT_TYPE )->GetLong( true );
			const tdata_type_defnition* defn = GetDataTypeDefn( type );
			if (ftype == 3 || ftype == 6)
				type = defn->screen_type;
			else
				type = defn->database_type;
			TransBuf->GetField( FLD_DATA_TYPE )->SetLong( type );
		};

		//@ Ensure data type range
		if (!TransBuf->GetField( FLD_DATA_TYPE )->CheckInRange( MIN_TYPE, MAX_TYPE ))
			vars->Errors->Add( ERR_DATA_TYPE, SEV_CRITICAL );

		//@ SUB FixFieldType to ensure valid data type for format-type
		TransBuf->DebugDump();
		FixFieldType( format_buffer, field_buffer, TransBuf, vars, true );

		//@ SUB Determine the next available sequence number
		GetNextSequenceNbr( TransBuf, vars );

		//@ SUB DataStructureVerify to check if data structure can be added
		TransBuf->DebugDump();
		DataStructureVerify( TransBuf, VERIFY_ADD, format_buffer, field_buffer, vars, pro_mode );

		//@ Check for errors
		vars->Errors->Check();

		//@ SUB DataStructureAdd to add the DS record
		DataStructureAdd( TransBuf, format_buffer, field_buffer, vars );

		vars->buffer->MoveCorresponding( TransBuf, vars->Errors );
	} catch( ... ) {
		vars->myapp->CleanupObjects( 4, &TransBuf, &format_buffer, &field_buffer, &stmt );
		throw;
	};
	vars->myapp->CleanupObjects( 4, &TransBuf, &format_buffer, &field_buffer, &stmt );
};



void ChgDataStructureInternal( XCTransactionVars* vars, bool pro_mode ) {
	XCBuffer* TransBuf = NULL;
	XCBuffer* CheckBuf = NULL;
	XCBuffer* field_buffer = NULL;
	XCBuffer* format_buffer = NULL;

	bool ReviewOnly;

	try {
		ReviewOnly = vars->buffer->IsBlank( 3, FLD_FORMAT_NAME, FLD_FIELD_NAME, FLD_APPL_NBR );

		//@ Create a transaction (DATA_STRUCTURES) buffer
		TransBuf = vars->myapp->CreateBuffer( FMT_DATA_STRUCTURES, true );

		//@ Move screen items into transaction buffer
		TransBuf->MoveCorresponding( vars->buffer, vars->Errors );
		TransBuf->DebugDump();

		//@ Create a format descriptor buffer
		format_buffer = vars->myapp->CreateBuffer( FMT_FORMAT_DESCRIPTR, false );

		//@ Create a field descriptor buffer
		field_buffer = vars->myapp->CreateBuffer( FMT_FIELD_DESCRIPTOR, false );

		//@ SUB DSGetFieldFormatNames to load field/format information
		DSGetFieldFormatNames( vars, TransBuf, true, format_buffer, field_buffer );
		vars->Errors->Check();

		//@ SUB DataStructureVerify(CHANGE) to verify
		CheckBuf = TransBuf->Copy();

		DataStructureVerify( TransBuf, ReviewOnly ? VERIFY_REVIEW : VERIFY_CHANGE, format_buffer, field_buffer, vars, pro_mode, CheckBuf );
		vars->Errors->Check();

		//@ SUB FixFieldType to ensure correct data type
		FixFieldType( format_buffer, field_buffer, TransBuf, vars, true );

		TransBuf->DebugDump();
		CheckBuf->DebugDump();

		if (!ReviewOnly) {
			//@ Ensure that this is not a restricted field
			//@ ERR C CRITICAL_FIELD_DEL
			if (!pro_mode) {
				int critical = TransBuf->GetField( FLD_CRITICAL_FIELD )->GetLong( true );
				if ((critical & 2) == 2)
					vars->Errors->Add( ERR_CRITICAL_FIELD, SEV_CRITICAL );
			};

			//@ SUB DataStructureChange to issue transaction
			DataStructureChange( TransBuf, CheckBuf, vars );
		};

		//@ Check for validation errors
		vars->Errors->Check();

		//@ Return the buffer
		vars->buffer->MoveCorresponding( TransBuf, vars->Errors );
		vars->myapp->TransResponse( vars->result, vars->buffer, TRANS_HEADER_TYPE );
	} catch( ... ) {
		vars->myapp->CleanupObjects( 4, &TransBuf, &CheckBuf, &format_buffer, &field_buffer );
		throw;
	};
	vars->myapp->CleanupObjects( 4, &TransBuf, &CheckBuf, &format_buffer, &field_buffer );
};



void DelDataStructureInternal( XCTransactionVars* vars, bool pro_mode ) {
	XCBuffer* TransBuf = NULL;
	XCBuffer* DBRelate = NULL;

	try {
		XCSQLGen* query = vars->query;

		//@ Create a transaction (DATA_STRUCTURES) buffer
		TransBuf = vars->myapp->CreateBuffer( FMT_DATA_STRUCTURES, false );

		//@ Move screen values into transaction buffer
		TransBuf->MoveCorresponding( vars->buffer, vars->Errors );

		//@ Check for validation errors
		vars->Errors->Check();

		//@ SUB DSGetFieldFormatNames to translate names to numbers
		DSGetFieldFormatNames( vars, TransBuf, true );
		vars->Errors->Check();

		TransBuf->DebugDump();

		//@ SUB DataStructureRetrieve to verify that data-structure exists
		if (!DataStructureRetrieve( TransBuf, vars ))
			vars->Errors->Add( ERR_DOESNT_EXIST, SEV_CRITICAL, FLD_FIELD_NAME, 1, 20014 );

		//@ Ensure that this is not a restricted field
		//@ ERR C CRITICAL_FIELD_DEL
		if (!pro_mode) {
			int critical = TransBuf->GetField( FLD_CRITICAL_FIELD )->GetLong( true );
			if ((critical & 1) == 1)
				vars->Errors->Add( ERR_CRITICAL_FIELD_DEL, SEV_CRITICAL );
		};

		//@ Create a DB_RELATE buffer
		DBRelate = vars->myapp->CreateBuffer( FMT_DB_RELATE, false );

		//@ Move DataStructure record values into DBRelate
		DBRelate->MoveCorresponding( TransBuf, vars->Errors );

		//@ Delete any DBRelate records dealing with this field (any but foreign)
		query->MakeDELETE( DBRelate );
		query->AddWHERE( 3, 
			DBRelate->GetField( FLD_APPL_NBR ),
			DBRelate->GetField( FLD_FORMAT_NUMBER ),
			DBRelate->GetField( FLD_FIELD_NUMBER ));
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

		//@ Move FORMAT_NUMBER into TGT_FORMAT_NUMBER
		DBRelate->MoveUnlikeField( FLD_TGT_FORMAT, TransBuf, FLD_FORMAT_NUMBER );

		//@ Move FIELD_NUMBER into TGT_FIELD
		DBRelate->MoveUnlikeField( FLD_TGT_FIELD, TransBuf, FLD_FIELD_NUMBER );

		//@ Delete any DBRelate records dealing with this field (FOREIGN KEYS)
		query->MakeDELETE( DBRelate );
		query->AddWHERE( 3, 
			DBRelate->GetField( FLD_APPL_NBR ),
			DBRelate->GetField( FLD_TGT_FORMAT ),
			DBRelate->GetField( FLD_TGT_FIELD ));
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );

		//@ Delete the DATA_STRUCTURES record
		query->MakeFull( SQL_STMT_DELETE, TransBuf );
		vars->dbtxn->ExecuteSQL( query, SQL_NO_RESULT );
	} catch( ... ) {
		vars->myapp->CleanupObjects( 2, &TransBuf, &DBRelate );
		throw;
	};
	vars->myapp->CleanupObjects( 2, &TransBuf, &DBRelate );
};
